package com.mapforge.map.tools.selection;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Vector;

import com.mapforge.selection.ComparablePoint;
import com.mapforge.selection.ComparableRectangle;
import com.mapforge.selection.ContinuousSelection;
import com.mapforge.selection.Selection;

public class TileSelectionCollection implements Selection {
	private Set<ComparableRectangle> selection;
	private Rectangle boundingRectangle;

	public TileSelectionCollection() {
		selection = new HashSet<ComparableRectangle>();
		boundingRectangle = new Rectangle();
	}

	public boolean add(Selection other) {
		if (other instanceof ContinuousSelection) {
			Rectangle selectionRect = other.getBoundingRectangle();
			boundingRectangle = boundingRectangle.union(selectionRect);
			Iterator<ComparableRectangle> rectIT = selection.iterator();
			ComparableRectangle rect;
			List<ComparableRectangle> tempList = new ArrayList<ComparableRectangle>(
					4);
			boolean brokeOut = false;
			while (rectIT.hasNext()) {
				rect = rectIT.next();
				tempList.clear();
				if (rect.contains(selectionRect)) {
					brokeOut = true;
					break;
				}
				Rectangle intersection = new ComparableRectangle(
						rect.intersection(selectionRect));
				if (!intersection.isEmpty()) {
					// System.out.println("Intersection not empty.");
					int xdiff = intersection.x - rect.x;
					int ydiff = intersection.y - rect.y;
					int rightDiff = (rect.x + rect.width)
							- (intersection.x + intersection.width);
					int bottomDiff = (rect.y + rect.height)
							- (intersection.y + intersection.height);

					ComparableRectangle top = new ComparableRectangle(rect.x,
							rect.y, rect.width, ydiff);
					ComparableRectangle right = new ComparableRectangle(
							intersection.x + intersection.width, rect.y,
							rightDiff, rect.height);
					ComparableRectangle bottom = new ComparableRectangle(
							rect.x, intersection.y + intersection.height,
							rect.width, bottomDiff);
					ComparableRectangle left = new ComparableRectangle(rect.x,
							rect.y, xdiff, rect.height);

					// System.out.println("TOP = " + top);
					// System.out.println("BOTTOM = " + bottom);
					// System.out.println("LEFT = " + left);
					// System.out.println("RIGHT = " + right);

					if (!top.isEmpty() && !top.intersects(intersection))
						tempList.add(top);
					if (!bottom.isEmpty() && !bottom.intersects(intersection))
						tempList.add(bottom);
					if (!right.isEmpty() && !right.intersects(intersection))
						tempList.add(right);
					if (!left.isEmpty() && !left.intersects(intersection))
						tempList.add(left);
					for (int i = 0; i < tempList.size(); i++) {
						ComparableRectangle tempRect = tempList.get(i);
						ComparableRectangle tempRectj;
						for (int j = i + 1; j < tempList.size(); j++) {
							tempRectj = tempList.get(j);
							Rectangle b = tempRect.intersection(tempRectj);
							if (!b.isEmpty()) {
								if (b.height == tempRect.height
										&& b.y == tempRect.y) {
									tempRect.width -= b.width;
									if (b.x == tempRect.x) {
										tempRect.x += b.width;
									}
								} else if (b.width == tempRect.width
										&& b.x == tempRect.x) {
									tempRect.height -= b.height;
									if (b.y == tempRect.y) {
										tempRect.y += b.height;
									}
								}
							}
						}
					}
					for (int i = 0; i < tempList.size(); i++) {
						selection.add(tempList.get(i));
					}
					selection.remove(rect);
				}
			}
			// only add selection if no rectangle contained it.
			if (!brokeOut)
				selection.add(new ComparableRectangle(selectionRect));
			return true;
		} else {
			throw new UnsupportedOperationException(
					"Only supports addition of Continuous Selections");
		}
	}

	@Override
	public Iterator<ComparablePoint> iterator() {
		return new RectangleIterator();
	}

	public Iterator<? extends Rectangle> rectangleIterator() {
		return selection.iterator();
	}

	@Override
	public void setOrigin(int x, int y) {

	}

	@Override
	public boolean subtract(Selection other) {
		return false;
	}

	private class RectangleIterator implements Iterator<ComparablePoint> {
		Iterator<ComparableRectangle> rectIT;
		Rectangle currentRectangle;
		int right, bottom;
		int x, y;

		public RectangleIterator() {
			List<ComparableRectangle> rectList = new Vector<ComparableRectangle>(
					selection);
			Collections.sort(rectList);
			rectIT = rectList.iterator();
			x = y = 0;
			if (rectIT.hasNext()) {
				currentRectangle = rectIT.next();
				x = currentRectangle.x;
				y = currentRectangle.y;
				right = x + currentRectangle.width;
				bottom = y + currentRectangle.height;
			} else {
				right = bottom = -1;
			}
		}

		@Override
		public boolean hasNext() {
			if (currentRectangle != null
					&& (x < currentRectangle.x + currentRectangle.width || y < currentRectangle.y
							+ currentRectangle.height)) {
				return true;
			} else {
				return rectIT.hasNext();
			}
		}

		@Override
		public ComparablePoint next() {
			if (currentRectangle != null) {
				if (x < right) {
					ComparablePoint p = new ComparablePoint(x, y);
					++x;
					return p;
				} else {
					x = currentRectangle.x;
					++y;
					if (y < bottom) {
						ComparablePoint p = new ComparablePoint(x, y);
						++x;
						return p;
					}
					// This rectangle is out of points, get another
					else {
						if (rectIT.hasNext()) {
							// ensure rectangle isn't empty, just in case
							while ((currentRectangle = rectIT.next()).isEmpty())
								;
							x = currentRectangle.x;
							y = currentRectangle.y;
							right = x + currentRectangle.width;
							bottom = y + currentRectangle.height;
							ComparablePoint p = new ComparablePoint(x, y);
							x++;
							return p;
						} else {
							throw new NoSuchElementException(
									"Selection has no more points");
						}
					}
				}
			} else {
				throw new NoSuchElementException("Selection has no points");
			}
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException(
					this.getClass().getName()
							+ " does not support removing points.\n\tUse subtract(Selection) instead.");
		}

	}

	@Override
	public void clearSelection() {
		selection.clear();
		boundingRectangle.setFrame(0, 0, 0, 0);
	}

	@Override
	public boolean contains(int x, int y) {
		Iterator<? extends Rectangle> rectIT = this.rectangleIterator();
		Rectangle r;
		while (rectIT.hasNext()) {
			r = rectIT.next();
			if (r.contains(x, y)) {
				return true;
			}
		}
		return false;
	}

	public boolean contains(Point p) {
		return contains(p.x, p.y);
	}

	@Override
	public Rectangle getBoundingRectangle() {
		return boundingRectangle;
	}

	@Override
	public boolean intersect(Selection other) {
		return false;
	}

	@Override
	public boolean isEmpty() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Iterator<ComparablePoint> iterator(Rectangle rectangle) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Selection addition(Selection other) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Selection subtraction(Selection other) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Selection intersection(Selection other) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean intersects(Selection other) {
		// TODO Auto-generated method stub
		return false;
	}
}
